#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <vector>
// #include <boost/lexical_cast.hpp>
// #include <boost/algorithm/string.hpp>

// #include <boost/thread/xtime.hpp>
// #include <boost/thread/thread.hpp>
#include <boost/array.hpp>
#include <boost/asio.hpp>

#include "base/logging.h"
#include "fcgi_service/fcgi_service.h"
using boost::asio::ip::tcp;

using namespace std;
using namespace fcgi;

class LinkProxyRequest : public Request {
public:
  LinkProxyRequest(FCGX_Request* r) : Request(r) {}
  virtual bool Response();
private:
  string BuildUpRequest(string * up_req);
};

string LinkProxyRequest::BuildUpRequest(string * up_req) {
  const char * prefix = "http://";
  string src = GetQuery("src");
  string host;
  if (strncmp(src.c_str(), prefix, strlen(prefix)) != 0) {
    cout << "req error!" << endl;
    return host;
  }
  size_t pos = strlen(prefix);
  while(src[pos] != '/') {
    host.push_back(src[pos]);
    ++pos;
  }

  if (host == "s10.sinaimg.cn") {
    host == "s1.sinaimg.cn";
  }

  *up_req =  "GET " + string(src.c_str() + pos) + " HTTP/1.1\r\n";
  *up_req += "Host:" + host + "\r\n";
  *up_req += "Connection: Close\r\n";
  
  string ua = GetParam("HTTP_USER_AGENT");
  if (!ua.empty()) {
    *up_req += "User-Agent: " + ua + "\r\n";
  }
  string accept = GetParam("HTTP_ACCEPT");
  if (!accept.empty()) {
    *up_req += "Accept: " + accept + "\r\n";
  }
  string ae = GetParam("HTTP_ACCEPT_ENCODING");
  if (!ae.empty()) {
    *up_req += "Accept-Encoding: " + ae + "\r\n";
  }
  string ac = GetParam("HTTP_ACCEPT_CHARSET");
  if (!accept.empty()) {
    *up_req += "Accept-Charset: " + ac + "\r\n";
  }

  string ref = GetQuery("ref");
  if (ref.empty()) {
    *up_req += "Referer: " + src + "\r\n";
  } else {
    *up_req += "Referer: " + ref + "\r\n";
  }

  *up_req += "\r\n";
  LOG_INFO(*up_req);
  cout << "req:" << up_req->c_str() << endl;
  return host;
}

bool LinkProxyRequest::Response() {
  if(!fcgi_out_) {
    cout << "fcgi_out null" << endl;
    return false;
  }
  cout << "new req." << endl;
  string up_req;
  string host = BuildUpRequest(&up_req);
  if (host.empty()) {
    return false;
  }
  try {
    boost::asio::io_service io_service;

    tcp::resolver resolver(io_service);
    tcp::resolver::query query(host, "80");
    // http://files.cyberciti.biz/cbzcache/3rdparty/unix-logo.gif
    // ref = http://www.cyberciti.biz/faq/howto-linux-bsd-unix-set-dns-nameserver/

    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
    tcp::resolver::iterator end;

    tcp::socket socket(io_service);
    boost::system::error_code error = boost::asio::error::host_not_found;
    while (error && endpoint_iterator != end) {
      socket.close();
      socket.connect(*endpoint_iterator++, error);
    }
    if (error) {
      throw boost::system::system_error(error);
    }

    int len = boost::asio::write(socket, boost::asio::buffer(up_req.c_str(), up_req.size()));
    cout << len << " - " << up_req.size() << endl;
    boost::array<char, 1024 * 8> buf;
    int recv_count = 0;
    // boost::system::error_code error;
    for (;;)
    {
      cout << "to get " << endl;
      size_t len = socket.read_some(boost::asio::buffer(buf), error);
      cout << "get " << len << endl;

      if (error == boost::asio::error::eof) {
        break; // Connection closed cleanly by peer.
      } else if (error) {
        throw boost::system::system_error(error); // Some other error.
      }
      FCGX_PutStr(buf.data(), len, fcgi_out_);
      LOG_DEBUG("read data " << len);
      recv_count += len;
      cout << "read data size " << len << "/" << recv_count << endl;
    }
  } catch (std::exception& e) {
    LOG_INFO("proxy error : " << e.what());
    cout << "error " << e.what() << endl;
    return false;
  }
  return true;
}

class ProxyRequestFactory : public RequestFactory {
public:
  virtual RequestPtr Create(FCGX_Request * r) {
    char * path = FCGX_GetParam("SCRIPT_NAME", r->envp);
    RequestPtr req;
    if (path) {
      if (strcmp(path, "/show-img") == 0) {
        req = RequestPtr(new LinkProxyRequest(r));
      }
    }
    return req;
  }
};

int main(){
  LOG_INIT("./link_proxy.log", "DEBUG");
  FcgiServer * fcgi_server = new FcgiServer("127.0.0.1:19002", 16);
  cout << "server start ..." << endl;
  // FcgiServer * fcgi_server = new FcgiServer("10.22.206.157:19000", 16);
  fcgi_server->RegisterRequestFactory(RequestFactoryPtr(new ProxyRequestFactory()));
  fcgi_server->Start(true); // 本线程也处理 fcgi 请求，不退出
  return 0;
}

